package com.bridgeintelligent.tag.webserver.dim.mgmt.service.export.excel;

import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.bridgeintelligent.tag.constants.ExceptionCodes;
import com.bridgeintelligent.tag.mapper.DimAggMapper;
import com.bridgeintelligent.tag.remotefile.RemoteFileHandler;
import com.bridgeintelligent.tag.webserver.dim.mgmt.api.DimException;
import com.bridgeintelligent.tag.webserver.dim.mgmt.api.export.DimAggData;
import com.bridgeintelligent.tag.webserver.dim.mgmt.api.export.ExcelDimDataModel;
import com.bridgeintelligent.tag.webserver.dim.mgmt.api.export.ExcelDimItemModel;
import com.bridgeintelligent.tag.webserver.dim.mgmt.api.export.ExcelReaderCheckRsp;
import com.wayneleo.quickstart.framework.ExceptionCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author JackCheung
 * @date 2020/7/6 3:45 PM.
 * description:
 */
@Slf4j
@Service
public class DimExcelReaderServiceImpl implements IDimExcelReaderService {

    private RemoteFileHandler remoteFileHandler;
    private DimAggMapper dimAggMapper;
    @Value("${nas.remoteStorage}")
    String remoteStorage;
    private IDimExcelDimCatalogService dimExcelDimCatalogService;
    private SqlSessionTemplate sqlSessionTemplate;

    public DimExcelReaderServiceImpl(RemoteFileHandler remoteFileHandler, DimAggMapper dimAggMapper,
                                     IDimExcelDimCatalogService dimExcelDimCatalogService,
                                     SqlSessionTemplate sqlSessionTemplate) {
        this.remoteFileHandler = remoteFileHandler;
        this.dimAggMapper = dimAggMapper;
        this.dimExcelDimCatalogService = dimExcelDimCatalogService;
        this.sqlSessionTemplate = sqlSessionTemplate;
    }

    /**
     * 上传excel文件，并获取变化结果
     *
     * @param excelFile excelFile
     * @return ExcelReaderCheckRsp
     */
    @Override
    public ExcelReaderCheckRsp upload(MultipartFile excelFile) {
        List<ExcelDimDataModel> dimDataModelList;
        List<ExcelDimItemModel> dimItemModelList;
        try {
            dimDataModelList = createDimList(excelFile);
            dimItemModelList = createDimItemList(excelFile);
        } catch (Exception e) {
            log.error("解析Excel出现异常：", e);
            throw new DimException(ExceptionCodes.EXPORT_70001);
        }

        if (CollectionUtils.isEmpty(dimDataModelList)) {
            throw new DimException(ExceptionCodes.EXPORT_70005);
        }
        dimExcelDimCatalogService.createCatalog(dimDataModelList);
        return dataHandler(dimDataModelList, dimItemModelList, excelFile);
    }

    @Transactional(rollbackFor = Exception.class)
    public ExcelReaderCheckRsp dataHandler(List<ExcelDimDataModel> dimDataModelList,
                                           List<ExcelDimItemModel> dimItemModelList, MultipartFile excelFile) {
        try {
            Map<String, List<DimAggData>> excelData = createAggMap(createUpdateData(dimDataModelList,
                                                                                    dimItemModelList));
            Map<String, List<DimAggData>> dbData = createAggMapInMap(dimAggMapper.findDimAggExist(null),
                                                                     dimExcelDimCatalogService.fetchCatalog());
            ExcelReaderCheckRsp result = new ExcelReaderCheckRsp();
            List<DimAggData> changeData = fetchChange(excelData, dbData, result);
            dimAggMapper.truncate();
            SqlSession sqlSession = sqlSessionTemplate.getSqlSessionFactory()
                                                      .openSession(ExecutorType.BATCH, false);
            DimAggMapper dimAggMapperBatcg = sqlSession.getMapper(DimAggMapper.class);
            changeData.forEach(dimAggMapperBatcg::saveChangeData);
            // 更新序号
            dimDataModelList.forEach(excelDimDataModel -> {
                if (excelDimDataModel != null && StringUtils.isNotBlank(excelDimDataModel.getDimId())) {
                    dimAggMapperBatcg.updateSortNum(excelDimDataModel);
                }
            });
            try {
                sqlSession.commit();
                sqlSession.clearCache();
            } catch (Exception e) {
                sqlSession.rollback();
                log.error("dataHandler报错:", e);
                throw new DimException(new ExceptionCode(70004, "标签批量导入出现异常!"));
            } finally {
                sqlSession.close();
            }
            remoteFileHandler.upload(remoteFileHandler.writeLocalWithFileName(excelFile, FILE_NAME_TMP), FILE_NAME);
            return result;
        } catch (DimException e) {
            log.error("存储发生异常---->>>", e);
            throw e;
        } catch (Exception e) {
            log.error("存储发生异常---->>>", e);
            throw new DimException(new ExceptionCode(70004, "标签批量导入出现异常!"));
        }
    }

    /**
     * @param excelData           上传的数据
     * @param dbData              数据库中现存的数据
     * @param excelReaderCheckRsp excelReaderCheckRsp
     * @return List<DimAggData> 获取变化的数据
     */
    private List<DimAggData> fetchChange(Map<String, List<DimAggData>> excelData, Map<String, List<DimAggData>> dbData,
                                         ExcelReaderCheckRsp excelReaderCheckRsp) {
        AtomicInteger inserted = new AtomicInteger(0);
        AtomicInteger updated = new AtomicInteger(0);
        List<DimAggData> result = new ArrayList<>(excelData.size());
        excelData.forEach((key, value) -> 
                addChangedData(key, value, dbData, result, inserted, updated));
        excelReaderCheckRsp.setInserted(inserted.get())
                           .setUpdated(updated.get());
        return result;
    }

    private void addChangedData(String key, List<DimAggData> value, Map<String, List<DimAggData>> dbData,
                                List<DimAggData> result, AtomicInteger inserted, AtomicInteger updated) {
        Date date = null;
        Date date1 = null;
        DateFormat format1 = new SimpleDateFormat("yyyy-MM-dd");
        if (dbData.containsKey(key)) {
            boolean isSizeEqual = (value.size() == dbData.get(key)
                                                         .size());
            if (isSizeEqual) {
                List<DimAggData> dbAggList = dbData.get(key);
                try {
                    date = format1.parse(value.get(0)
                                              .getUptTime());
                    date1 = format1.parse(dbAggList.get(0)
                                                   .getUptTime());
                } catch (ParseException e) {
                    log.info("标签：{}时间转换异常：{}", key, e.getMessage());
                    e.printStackTrace();
                }
                value.get(0)
                     .setUptTime(format1.format(date));
                dbAggList.get(0)
                         .setUptTime(format1.format(date1));
                //首先比较基本信息
                if (!value.get(0)
                          .equalsWithoutItem(dbAggList.get(0))) {
                    log.info("标签：{}基础信息出现变动", key);
                    value.forEach(updateAggData -> updateAggData.setType(DIM_UPDATE));
                    result.addAll(value);
                    updated.getAndIncrement();
                } else {
                    //基本信息相同的情况下再做item的比较
                    Map<String, String> itemNoMap;
                    try {
                    itemNoMap = dbAggList.stream()
                                         .filter(dimAggData -> StringUtils.isNotEmpty(dimAggData.getItemNo()))
                                         .collect(Collectors.toMap(DimAggData::getItemNo, DimAggData::getItemName));
                    }
                    catch ( Exception e ) {
                        e.printStackTrace();
                        throw new RuntimeException();
                    }
                    for (DimAggData dimAggData : value) {
                        if (StringUtils.isNotEmpty(dimAggData.getItemNo())) {
                            boolean isContainOrEqual = (!itemNoMap.containsKey(dimAggData.getItemNo())) ||
                                                       (!itemNoMap.get(dimAggData.getItemNo())
                                                                  .equals(dimAggData.getItemName()));
                            if (isContainOrEqual) {
                                value.forEach(updateAggData -> updateAggData.setType(DIM_UPDATE));
                                result.addAll(value);
                                updated.getAndIncrement();
                                break;
                            }
                        }
                    }
                }
            } else {
                value.forEach(updateAggData -> updateAggData.setType(DIM_UPDATE));
                result.addAll(value);
                updated.getAndIncrement();
            }
        } else {
            value.forEach(updateAggData -> updateAggData.setType(DIM_INSERT));
            result.addAll(value);
            inserted.getAndIncrement();
        }
    }

    /**
     * 将标签数据和标签值对应
     *
     * @param dimDataModelList 标签数据模型list
     * @param dimItemModelList 标签值list
     * @return List<DimAggData>
     */
    private List<DimAggData> createUpdateData(List<ExcelDimDataModel> dimDataModelList,
                                              List<ExcelDimItemModel> dimItemModelList) {
        List<DimAggData> result = new ArrayList<>(dimItemModelList.size());
        Map<String, List<ExcelDimItemModel>> itemMap = dimItemModelList.stream()
                                                                       .collect(Collectors.groupingBy(
                                                                               ExcelDimItemModel::getDimId));
        dimDataModelList.forEach(dimDataModel -> {
            List<ExcelDimItemModel> itemModelList = itemMap.get(dimDataModel.getDimId());
            if (CollectionUtils.isNotEmpty(itemModelList)) {
                itemModelList.forEach(dimItemModel -> result.add(dimDataModel.converToDimAgg(dimDataModel,
                                                                                             dimItemModel)));
            } else {
                result.add(dimDataModel.converToDimAgg(dimDataModel, null));
            }
        });
        return result.stream()
                     .filter(dimAggData -> fetchDate(dimAggData.getEffectiveTime()))
                     .collect(Collectors.toList());
    }

    /**
     * 读取标签数据模型sheet页
     *
     * @param excelFile excel
     * @return List<ExcelDimItemModel>
     * @throws IOException IOException
     */
    private List<ExcelDimItemModel> createDimItemList(MultipartFile excelFile) throws IOException {
        List<ExcelDimItemModel> results = new ArrayList<>();
        DimExcelListener<ExcelDimItemModel> dimExcelListener = new DimExcelListener<>(data -> {
            // 处理你的业务逻辑，最好抽出一个方法单独处理逻辑
            List<ExcelDimItemModel> collect = data.stream()
                                                  .filter(excelDimItemModel -> StringUtils.isNotEmpty(
                                                          excelDimItemModel.getDimId()))
                                                  .map(excelDimItemModel -> {
                                                      excelDimItemModel.setDimId(
                                                              StringUtils.trim(excelDimItemModel.getDimId()));
                                                      return excelDimItemModel;
                                                  }).collect(Collectors.toList());
            results.addAll(collect);
        });
        try (ExcelReader excelReader = EasyExcelFactory.read(excelFile.getInputStream()).build()) {

            ReadSheet dealerSheet = EasyExcelFactory
                    .readSheet(2)
                    .head(ExcelDimItemModel.class)
                    .registerReadListener(dimExcelListener)
                    .headRowNumber(6)
                    .build();

            excelReader.read(dealerSheet);
            return results;
        } catch (IOException e) {
            log.error("处理标签excel失败，" + e.getMessage());
            throw new IOException(e);
        }
    }

    /**
     * 读取标签值sheet页
     *
     * @param excelFile excel
     * @return List<ExcelDimDataModel>
     * @throws IOException IOException
     */
    private List<ExcelDimDataModel> createDimList(MultipartFile excelFile) throws IOException {
        List<ExcelDimDataModel> results = new ArrayList<>();
        DimExcelListener<ExcelDimDataModel> dimExcelListener = new DimExcelListener<>(data -> {
            // 处理你的业务逻辑，最好抽出一个方法单独处理逻辑
            List<ExcelDimDataModel> collect = data.stream()
                                                  .filter(excelDimDataModel -> StringUtils.isNotEmpty(
                                                          excelDimDataModel.getDimId()))
                                                  .map(excelDimDataModel -> {
                                                      excelDimDataModel.setDimId(
                                                              StringUtils.trim(excelDimDataModel.getDimId()));
                                                      return excelDimDataModel;
                                                  }).collect(Collectors.toList());
            results.addAll(collect);
        });
        try (ExcelReader excelReader = EasyExcelFactory.read(excelFile.getInputStream()).build()) {

            ReadSheet dealerSheet = EasyExcelFactory
                    .readSheet(1)
                    .head(ExcelDimDataModel.class)
                    .registerReadListener(dimExcelListener)
                    .headRowNumber(3)
                    .build();

            excelReader.read(dealerSheet);
            return results;
        } catch (IOException e) {
            log.error("处理标签excel失败，" + e.getMessage());
            throw new IOException(e);
        }
    }
}
